﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TiposComuns.Objectos;
using System.Collections;

namespace Servidor.Objectos.Pesquisa
{
    /// <summary>
    /// Contains a map between the hash of a searched property,
    /// and the list of people that have it, to a maximum value.
    /// It also contains the information required to continue this list.
    /// </summary>
    [Serializable]
    public class SearchedNodes
    {
        #region Consts
        public const int MAX = 100;
        #endregion

        #region Constructors

        public SearchedNodes()
        {
        }
        #endregion

        #region Attributes
        private SerializableDictionary<int, SearchResult> map =
            new SerializableDictionary<int, SearchResult>();
        private SerializableDictionary<int, byte[]> keys =
            new SerializableDictionary<int, byte[]>();

        public SerializableDictionary<int, SearchResult> Map
        {
            get { return map; }
            set { map = value; }
        }

        public SerializableDictionary<int, byte[]> Keys
        {
            get { return keys; }
            set { keys = value; }
        }

        #endregion

        #region Methods

        public void Remove(byte []key)
        {
            lock (this)
            {
                int s = GetKey(key);
                this.map.Remove(s);
                this.keys.Remove(s);
            }
        }

        public int GetKey(byte []key)
        {
            return ASCIIEncoding.Default.GetString(key).GetHashCode();
        }
        /// <summary>
        /// Adds a new entry to the list of nodes
        /// </summary>
        /// <param name="key">Key of the property</param>
        /// <param name="person">New person</param>
        /// <returns>True if the list wasn't full, false otherwise</returns>
        public void Add(byte[] key, Person person)
        {
            lock (this)
            {
                int s = GetKey(key);
                if (this.map.ContainsKey(s))
                    this.map[s].Add(person);
                else
                {
                    SearchResult sr = new SearchResult(key);
                    this.map.Add(s, sr);
                    sr.Add(person);
                    this.keys.Add(s, key);
                }
            }
        }

        /// <summary>
        /// Tries to remove a person from the map.
        /// If it doesn't contain it, returns the rest
        /// of the global map.
        /// </summary>
        /// <param name="key">Property's key</param>
        /// <param name="person">Person to remove</param>
        /// <returns>Null, if it could remove it, or a pair composed of the
        /// key and list of person that might store the person to remove</returns>
        public void Remove(byte []key,Person person)
        {
            lock (this)
            {
                int s = GetKey(key);
                if (map.ContainsKey(s))
                {
                    map[s].Remove(person);
                }
            }
        }


        /// <summary>
        /// Sets the metadata related to the continuation of
        /// the distributed map.
        /// </summary>
        /// <param name="contKey">Continuation key</param>
        /// <param name="nexts">K closest nodes to contKey</param>
        public void SetNexts(byte []key, byte []contKey, KClosest nexts)
        {
            lock (this)
            {
                int s = GetKey(key);
                if (this.map.ContainsKey(s))
                    this.map[s].SetNexts(contKey, nexts);
            }
        }

        public SearchResult Get(byte[] key)
        {
            lock (this)
            {
                int s = GetKey(key);
                if (map.ContainsKey(s)) return map[s];
                else return null;
            }
        }

        public void Merge(byte []key, SearchResult sr)
        {
            lock (this)
            {
                int s = GetKey(key);
                if (!map.ContainsKey(s))
                {
                    this.map.Add(s, sr);
                }
                else
                {
                    this.map[s].Merge(sr);
                }
            }
        }

        #endregion
    }
}
